#
# P4Log.py
#
# Wrapper around Perforce filelog SNiFF+ 3.x Perforce Adapter
#
# Author: Martin Oberhuber
# Copyright (c) 1998, 1999 TakeFive Software, Salzburg
#
# Revisions:
#    1999/07/29  mob  Initial Version copied from CVS
#

import string
from vcs import TRUE, FALSE, NL
from logParser import Log, Revision
from P4 import P4Iter
import P4Prefs

SNIFF_BRANCH_PREFIX = 'SNIFF_BRANCH_'
SNIFF_CHANGESET_PREFIX = 'SNIFF_CHANGESET_'

def UnCodeRev(csRev):
	"""uncode a changeset name into a revision"""
	return csRev
	
def CodeRev(p4Rev):
	"""code a revision for use as a version name by escaping the dot"""
	return p4Rev
	csRev = string.join(string.split(p4Rev,"_"),"__")
	csRev = string.join(string.split(csRev,"."),"_d")
	return csRev

class P4Revision(Revision):
	def __init__(self, p4rev, sniffrev=""):
		if sniffrev:
			Revision.__init__(self, sniffrev)
			self.p4rev = p4rev
		else:
			Revision.__init__(self, "1."+p4rev)
			#self.p4rev = "#"+p4rev
			self.p4rev = p4rev
		self.changenum = ''   # changeset number
		self.changetype = ''  # branch or edit
		self.flags = ''       # text, ktext etc.
		self.log_message = '' # comment
		self.branchlist = []  # list of branches
		self.branchtext = []  # list of branchtexts
		self.branchcount = 0  # counter for offbranches
	def append_comment(self, comm):
		if self.log_message:
			self.log_message = self.log_message + NL
		self.log_message = self.log_message + comm
	def append_branch(self, br):
		self.branchtext.append(br)
		if br[:11] == "branch into":
			theBranch = br[12:]
			brType = "branch into"
		elif br[:9] == "copy into":
			theBranch = br[10:]
			brType = "copy into"
		elif br[:11] == "branch from":
			theBranch = br[12:]
			brType = "branch from"
		elif br[:9] == "copy from":
			theBranch = br[10:]
			brType = "copy from"
		else:
			return
		self.branchcount = self.branchcount + 1
		brSniffRev = "%s.%s.1" % (self.rev, self.branchcount)
		# copy over: don't create branchname but use long rev
		brList=string.split(theBranch, "#")
		brRev=brList[1]
		brName = CodeRev(brList[0]+"#"+brRev)
		#brName = CodeRev(brList[0])
		self.branchlist.append(brType, brName, brRev, brSniffRev)
	def __str__(self):
		if self.p4rev[:2]=="//":
			str = "%s change %s " % (self.p4rev, self.changenum)
		else:
			str = "#%s change %s " % (self.p4rev, self.changenum)
		if self.locked_by:
			str = str + '\tlocked by: ' + self.locked_by
		str = str + "(" + self.flags + ")\n"
		str = str + "%s on %s by %s" % (self.changetype, self.date, self.author)
		for curBranch in self.branchtext:
			str = str + "\n... " + curBranch
		str = str + NL + self.log_message
		return str
	__repr__ = __str__

class P4log(Log):
	CMD		= '-d %s filelog -l'
	def __init__(self, workingDir, repoFiles, workingFiles):
		# use workingDir as the "Option" argument which will override the
		# %s in CMD in Log()
		Log.__init__(self,repoFiles, workingFiles, workingDir)
	def open(self, cmd, file):
		self.log = P4Iter(cmd + ' ' + file)
		self.closed=FALSE
	def close(self):
		self.closed=TRUE
		return TRUE
	def parse_log(self, log):
		# copied from logParser
		self.valid = FALSE
		self.history = []
		self.revisions = []
		self.head = "1.1"
		line = log.readline()
		if line == None:
			self.closed=TRUE
			return
		while line and not string.lstrip(line):
			line = log.readline()
		if line == None:
			self.closed=TRUE
			return
		curRev = None
		while line != None:
			# // shows the beginning of a new file
			# if TEST: print line,
			if not line:
				# empty line: continue, reading the next line
				pass
			elif line[:2] == "//":
				if curRev:
					# next file in a multi-file log
					break
				if string.find(line, " - ") < 0:
					# no additional information, it's a regular line.
					self.repositoryFile = line
				else:
					if string.find(line, "- no such file") < 0:
						# this is an error!
						print "(P4Log) Error -- ignoring file:"
						print line
					# file is not yet checked in: construct an empty pseudo version for this file.
					self.repositoryFile = string.split(line)[0]
					self.history.append(line)
					curRev = P4Revision("missing","1.1")
					self.head=curRev.rev
					self.addRevision(curRev)
			else:
				# line should be part of the history
				if line[:5] == "... #":
					self.history.append(string.rstrip(line))
					if curRev:
						self.addRevision(curRev)
					try:
						# new revision: build indices
						i1 = string.index(line, " ", 5)
						curRev = P4Revision(line[5:i1])
						# first revision is the newest one
						if self.head == "1.1":
							self.head = curRev.rev
						i2 = string.index(line, " ", i1+8)
						curRev.changenum = line[i1+8:i2]
						i3 = string.index(line, " on", i2)
						curRev.changetype = line[i2+1:i3]
						i4 = string.index(line, " ", i3+4)
						curRev.date = line[i3+4:i4]
						i5 = string.index(line, " ", i4+4)
						curRev.author = line[i4+4:i5]
						# try if this can avoid the sorting problem
						#curRev.author = string.split(curRev.author,'@')[0]
						i6 = string.index(line, ")", i5+2)
						curRev.flags = line[i5+2:i6]
					except:
						print "Error parsing Log -- erroneous line:"
						print line
				elif line[0] == "\t":
					# comment
					self.history.append(string.rstrip(line))
					curRev.append_comment(line[1:])
				elif line[:7] == "... ...":
					self.history.append(string.rstrip(line))
					curRev.append_branch(line[8:])
				elif line[:2] == "//":
					# beginning of a new file
					break
				else:
					print "Error parsing Log -- erroneous line:"
					print line
				# end if (in current revision)
			# end if (part of the history)
			line = log.readline()
		# end while (in current revision)
		if curRev:
			self.addRevision(curRev)
		if line == None:
			self.closed=TRUE
		else:
			# stopped because next entry was found
			log.unreadline()
		self.valid = TRUE
	def addRevision(self, rev):
		# helper function to add both branches and revision
		self.revisions.append(rev)
		if P4Prefs.TEST2: print "(P4log) addRevision: ", rev.rev, rev.p4rev
		for curBranch in rev.branchlist:
			(brType, brName, brRev, brSniffRev) = curBranch
			try:
				brFrom = string.split(brType)[1]
			except:
				brFrom = ""
			if brFrom == "from":
				# don't show from-branches
				continue
			P4Rev = P4Revision(brName, brSniffRev)
			# this is most probably not true, because the
			# branch can be created by a different person
			# than the version from which it is branched off.
			# but it seems SNiFF needs some information...
			P4Rev.changenum = "??"
			P4Rev.changetype = string.split(brType)[0]
			P4Rev.date = "1980/01/01"
			P4Rev.author = "???"
			#P4Rev.log_message = brType + " into " + brName + "#" + brRev
			P4Rev.log_message = brType + " " + brName
			self.revisions.append(P4Rev)
			if P4Prefs.TEST2: print "(P4log) addRevision: ",P4Rev.rev, P4Rev.p4rev
	def parse_history(self):
		# should never need to be called
		print "Error? P4Log.parse_history() called..."
	def head_rev(self):
		return self.head
	def revision(self, revision):
		for rev in self.revisions:
			if revision == rev.p4rev: 
				return rev
		return None
	def revision_list(self):
		return self.revisions
	def lockers(self):
		# exclusive locking is currently not implemented!
		return ""
	def symbollist(self):
		# result could be cached (self.symbol_list) similarly to
		# "filtering" in CVS...
		# could also add branches here.
		if not self.symbol_list:
			self.symbol_list = []
			self.branch_list = []
			for curRev in self.revisions:
				if curRev.changenum and curRev.changenum != "??":
					# Branch Revisions have no Changesets
					curCS = SNIFF_CHANGESET_PREFIX + curRev.changenum
					if curRev.changetype == "branch":
						curCS = curCS + "_B"
					curCS = curCS + "_" + P4Prefs.SymbolFromComment(curRev.log_message)
					curCS = curCS + ": " + curRev.rev
					self.symbol_list.append(curCS)
				for curBranch in curRev.branchlist:
					(brType, brName, brRev, brSniffRev) = curBranch
					shortBrName = P4Prefs.MakeBranchName(brName, self.repositoryFile)
					if not shortBrName in self.branch_list:
						self.branch_list.append(shortBrName)
						curBR = SNIFF_BRANCH_PREFIX + P4Prefs.SymbolFromComment(shortBrName)
						# create an entry for HEAD_branchname
						newP4Rev = string.split(brName,"#")[0] # +"#head"
						newSniffRev = string.join(string.split(brSniffRev,".")[:-1],".")+".2"
						P4Rev = P4Revision(newP4Rev, newSniffRev)
						P4Rev.changenum = "??"
						P4Rev.changetype = brType
						P4Rev.date = "1980/01/01"
						P4Rev.author = "???"
						#P4Rev.log_message = brType + " into " + brName
						P4Rev.log_message = "fake HEAD revision by SNiFF"
						self.revisions.append(P4Rev)
						curBR = curBR + ": " + newSniffRev
						self.symbol_list.append(curBR)
		return self.symbol_list
	def symbolstr(self):
		my_debug_symList = self.symbollist()
		if my_debug_symList:
			return string.join(my_debug_symList, NL)
		else:
			return ""
	def next(self):
		# when multiple files are concerned, need to re-convert for new file
		Log.next(self)
		self.symbol_list = None
	def work_file(self):
		# if Perforce returns files in same order as requested, 
		# this will just be fine: no basename / directory comparison
		return self.workFiles[self.count-1]
	def is_last_file(self):
		# returns whether this is the last in a list of files or there are more.
		nFiles = len(self.workFiles)
		if nFiles == 1 or self.count >= nFiles: 
			return TRUE
		return FALSE
